import numpy as np


class Layer:
    def __init__(self, n_input, n_neutrons, activation=None, weights=None, bias=None):
        """
        自定义的层
        :param n_input: 输入的节点数量
        :param n_neutrons: 输入的节点数量（自身神经元数量）
        :param activation: 激活函数
        :param weight: 权重
        :param bias: 偏置
        """
        # 通过正态分布初始化网络权值，初始化非常重要，不合适的初始化将导致网络不收敛
        self.weights = weights if weights is not None else np.random.randn(n_input, n_neutrons) * np.sqrt(1 / n_neutrons)
        self.bias = bias if bias is not None else np.random.rand(n_neutrons) * 0.1
        self.activation = activation  # 激活函数类型，如’sigmoid’
        self.last_activation = None  # 激活函数的输出值o
        self.error = None  # 用于计算当前层的delta 变量的中间变量
        self.delta = None  # 记录当前层的delta 变量，用于计算梯度

    def activate(self, x):
        """
        数据通过当前层
        :param x:
        :return:
        """
        # 前向运算
        result = np.dot(x, self.weights) + self.bias
        # 将前向运损结果通过 激活函数
        self.last_activation = self._apply_activation(result)
        return self.last_activation

    # 上述代码中的self._apply_activation 函数实现了不同类型的激活函数的前向计算过程，
    # 尽管此处我们只使用Sigmoid 激活函数一种。代码如下：
    def _apply_activation(self, r):
        """
        利用激活函数处理
        :param r:
        :return:
        """
        # 计算激活函数的输出
        if self.activation is None:
            return r  # 无激活函数，直接返回
        # ReLU 激活函数
        elif self.activation == 'relu':
            return np.maximum(r, 0)
        # tanh 激活函数
        elif self.activation == 'tanh':
            return np.tanh(r)
        # sigmoid 激活函数
        elif self.activation == 'sigmoid':
            return 1 / (1 + np.exp(-r))
        return r

    # 针对于不同类型的激活函数，它们的导数计算实现如下：
    def apply_activation_derivative(self, r):
        """
        计算当前层的梯度
        :param r:
        :return:
        """
        # 计算激活函数的导数
        # 无激活函数，导数为1
        if self.activation is None:
            return np.ones_like(r)
        # ReLU 函数的导数实现
        elif self.activation == 'relu':
            grad = np.array(r, copy=True)
            grad[r > 0] = 1.
            grad[r <= 0] = 0.
            return grad
        # tanh 函数的导数实现
        elif self.activation == 'tanh':
            return 1 - r ** 2
        # Sigmoid 函数的导数实现
        elif self.activation == 'sigmoid':
            return r * (1 - r)
        return r